package org.hxzon.demo.nio;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;

public class Client {
	int lastReqSeq;
	int lastReqCmd;

	private ByteBuffer buildRequest(int cmd, ByteBuffer paramBuffer) {
		paramBuffer.flip();
		ByteBuffer reqBuffer = ByteBuffer.allocate(16 + paramBuffer.remaining());
		lastReqSeq = (int) new Date().getTime() / 1000;
		reqBuffer.putInt(lastReqSeq);// 当前时间的秒数作为命令序列号
		lastReqCmd = cmd;
		reqBuffer.putInt(cmd);
		reqBuffer.putInt(0);
		reqBuffer.putInt(paramBuffer.remaining());

		reqBuffer.put(paramBuffer);
		reqBuffer.flip();
		return reqBuffer;
	}

	Selector selector;
	public static int TIMEOUT = 600000;
	Timer timer;

	private void init() throws IOException{
		selector = Selector.open();
		SocketChannel client = SocketChannel.open();
		client.configureBlocking(false);
		selector = Selector.open();
		client.register(selector, SelectionKey.OP_CONNECT);
		client.connect(new InetSocketAddress(80));
		
    	selector.select();
    	Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
//    	System.out.println("selector.selectedKeys():" + selector.selectedKeys().size());
    	while(iter.hasNext()){
    		SelectionKey key = iter.next();
    		iter.remove();
    		if (key.isConnectable()) {
    			SocketChannel channel = (SocketChannel) key.channel();   
    			if (channel.isConnectionPending()){
    				channel.finishConnect();
    			}
    			
    			channel.register(selector,  SelectionKey.OP_WRITE);
    			break;
    		}
    	}
	}
	private ByteBuffer requestAndWait(ByteBuffer reqBuffer)  {
		return requestAndWait2(reqBuffer, false);
	}

	private ByteBuffer requestAndWait2(ByteBuffer reqBuffer, boolean nowait)  {
		boolean first = true;
		int paramLen = 0;
		int totalRead = 0;
		ByteBuffer onceBuf = ByteBuffer.allocate(20 * 1024);// 512K
		try {
			while (true) {
				if (selector == null) {
					init();
				}
				selector.select();
				Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
				if (selector.selectedKeys().size() == 0) {
					throw new RuntimeException("发生超时,服务器在" + TIMEOUT / 1000 + "s内无响应,");
				}
				while (iter.hasNext()) {
					SelectionKey key = iter.next();
					iter.remove();
					if (key.isReadable()) {

						if (timer != null) {
							timer.cancel();
						}

						SocketChannel readChannel = (SocketChannel) key.channel();
						if (first) {
							onceBuf.clear();
						}
						int count = -1;
						while (count != 0) {
							count = readChannel.read(onceBuf);
							if (count == -1) {
								throw new IOException("Server closed count = -1");
							}
							totalRead += count;
							// logger.debug("channel.read count :" + count);
						}

						// onceBuf.flip();
						if (first) {
							ByteBuffer tmp = onceBuf.duplicate();
							tmp.flip();
							first = false;
							tmp.getInt();// int seq =
							tmp.getInt();// int cmd =
							tmp.getInt();// int reserve =
							paramLen = tmp.getInt();
							// logger.debug("preParse response: cmd:" + cmd +
							// ",param len:" + paramLen + ",total expect:" +
							// (paramLen + 16));
						}
						// logger.debug("total=" + totalRead);

						if (totalRead == paramLen + 16) {
							readChannel.register(selector, SelectionKey.OP_WRITE);
							onceBuf.flip();
							return onceBuf;
						}

					} else if (key.isWritable()) {
						// reqBuffer.flip();
						if (reqBuffer == null) {
							try {
								Thread.sleep(50);
							} catch (InterruptedException e) {
							}
						} else {
							ByteBuffer tmp = reqBuffer.duplicate();
							tmp.getInt();// int seq =
							tmp.getInt();// int cmd =
							SocketChannel writeChannel = (SocketChannel) key.channel();
							writeChannel.write(reqBuffer);
							// logger.debug("request wrote(cmd:" + cmd + ",seq:"
							// + seq + "),waiting for server response...");
							reqBuffer = null;

							if (timer != null) {
								timer.cancel();
							}
							timer = new Timer("SockCommunicator");
							timer.schedule(new TimerTask() {

								@Override
								public void run() {
									selector.wakeup();
								}

							}, TIMEOUT);

							writeChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
							if (nowait) {
								return null;
							}

						}

					}
				}
			}

		} catch (IOException e) {
			throw new ClientException(e);
		}
	}

	boolean stopReadPkt;

	public void queryReadPkt(int id, String filename, String savepath) {
		long startTime = System.currentTimeMillis();
		// int totalLen=0;
		OutputStream out;
		// logger.debug("download pkt,filename :"+filename);
		String f = filename.replaceAll("[/\\\\]", "_");
		// logger.debug("download pkt,save filename :"+savepath+f);
		try {
			int eof = 0;
			ByteBuffer paramBuffer;
			out = new FileOutputStream(new File(savepath + f));
			for (int i = 0; eof == 0 && !stopReadPkt; i++) {
				paramBuffer = ByteBuffer.allocate(8 + 8 + 4 + filename.length());
				paramBuffer.putInt(4);
				paramBuffer.putInt(id);
				paramBuffer.putInt(4);
				paramBuffer.putInt(i);// blockNum
				paramBuffer.putInt(filename.length());
				paramBuffer.put(filename.getBytes());

				ByteBuffer reqBuffer = buildRequest(CmdHeader.RequestDownload, paramBuffer);
				ByteBuffer respBuffer = requestAndWait(reqBuffer);
				CmdHeader cmdHeader = new CmdHeader(respBuffer);

				respBuffer.getInt();// eof len
				eof = respBuffer.getInt();
				int len = respBuffer.getInt();
				// totalLen+=len;
				// logger.debug("block num:"+i+",  data len:"+len);
				byte[] data = new byte[len];
				// logger.debug("respBuffer remaining:"+respBuffer.remaining());
				respBuffer.get(data);
				out.write(data);
			}
			out.close();
		} catch (BufferUnderflowException e) {
			throw new ClientException(e);
		} catch (IOException e) {
			throw new ClientException(e);
		}
		long endTime = System.currentTimeMillis();
		long spanTime = endTime - startTime;
		// logger.debug(",file size:"+totalLen);
	}
	
	public static void main(String args[]){
		Client client=new Client();
		client.queryReadPkt(0, "D:/export/17-9-18.pcap", "D:/export/17-43-1.pcap");
	}
}
